package com.wanho.qz103.hrsaas.commons.factory;

import com.wanho.qz103.hrsaas.commons.annontations.*;
import com.wanho.qz103.hrsaas.commons.proxy.TxProxy;
import com.wanho.qz103.hrsaas.commons.vo.Action;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.stream.Streams;
import org.reflections.Reflections;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 工厂类：管理所有bean  1.帮我们new @Service  2.属性自动赋值 @Autowired
 */
@Slf4j
public abstract class BeanFactory {
    /** 工厂：map 存放所有被管理的bean ： key ：接口或类的Class value : new 对象*/
    private static Map<Class,Object> beanMap = new HashMap<>() ;
    /** 请求路径： 对应 Action*/
    private static List<Action>  actionList = new ArrayList<>() ;
    private static List<Action>  equalsActionList  ;
    private static List<Action>  likeActionList  ;
    /**
     * 类加载：扫描 文件： new 动作， 赋值动作
     */
    static{
        try {
            //////////////////////////1.IOC 开始///////////////////////////////////
            //1. 扫描 拥有注解的类  Component Repository Service RestController
            Reflections reflections = new Reflections("com.wanho.qz103.hrsaas");
            // 扫描 工具类 数据层 业务层 控制层
            Set<Class<?>> componentClassSet = reflections.getTypesAnnotatedWith(Component.class);
            Set<Class<?>> repositoryClassSet = reflections.getTypesAnnotatedWith(Repository.class);
            Set<Class<?>> serviceClassSet = reflections.getTypesAnnotatedWith(Service.class);
            Set<Class<?>> restControllerClassSet = reflections.getTypesAnnotatedWith(RestController.class);
            //2. 通过反射 new实例 IOC
            // 放入集合中 有序集合
            Set<Class<?>> beanClassSet = new LinkedHashSet<>() ;
            beanClassSet.addAll(componentClassSet) ;
            beanClassSet.addAll(repositoryClassSet) ;
            beanClassSet.addAll(serviceClassSet) ;
            beanClassSet.addAll(restControllerClassSet) ;
            // 循环
            for (Class<?> cls : beanClassSet) {
                //创建cls实例  调用该类 无参构造  默认构造方法
                Object instance = cls.newInstance();
                //获得 key  当前被管理的类的接口数组
                Class<?>[] interfaces = cls.getInterfaces();
                // 默认 key 使用当前 类
                Class key = cls ;
                // 如果  当前类实现 接口，面向接口编程
                if (interfaces!=null && interfaces.length>0){
                    key = interfaces[0] ;
                }
                //3. 放入map中
                beanMap.put(key,instance) ;
            }
            /////////////////////////1. IOC结束/////////////////////

            /////////////////////////2.DI 开始//////////////////////////////
            // DI 依赖注入 被管理的bean的 属性进行赋值
            // 循环 所有bean   拥有 获得属性 @Autowired  进行赋值操作
            // 只循环 所有的 values
            for (Object instance : beanMap.values()) {
                //获得所有的属性
                Field[] declaredFields = instance.getClass().getDeclaredFields();
                // 拥有@Autowired的属性
                List<Field> fieldList = Stream.of(declaredFields).filter(field -> field.getAnnotation(Autowired.class) != null).collect(Collectors.toList());
                // 循环进行赋值
                fieldList.forEach(field->{
                    try {
                        //获得属性类型
                        Class<?> type = field.getType();
                        // 根据属性类型 到当前map中获得 该类型的实例  比如： @Autowired  private RoleDAO roleDAO ;
                        Object value = beanMap.get(type) ;
                        //设置该属性可被访问
                        field.setAccessible(true);
                        // 反射赋值
                        field.set(instance,value);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                });
            }
            ///////////////////////2.DI 结束//////////////////////////////


            ///////////////////////3.请求路径 Action 开始/////////////////////////////////////
            // 循环所有的 controller的注解  获得 method对象
            restControllerClassSet.forEach(controllerCls->{
                //找到 controller类中 所有的方法
                Method[] declaredMethods = controllerCls.getDeclaredMethods();
                //过滤 拥有注解的方法 GetMapping  PostMapping DeleteMapping  PutMapping
                List<Method> methodList = Stream.of(declaredMethods).filter(method -> method.getAnnotation(GetMapping.class) != null || method.getAnnotation(PostMapping.class) != null || method.getAnnotation(DeleteMapping.class) != null || method.getAnnotation(PutMapping.class) != null).collect(Collectors.toList());
                methodList.forEach(method->{
                    // 封装 Action
                    Action action = new Action();
                    GetMapping getAnnotation = method.getAnnotation(GetMapping.class) ;
                    PostMapping postAnnotation = method.getAnnotation(PostMapping.class) ;
                    PutMapping putAnnotation = method.getAnnotation(PutMapping.class) ;
                    DeleteMapping deleteAnnotation = method.getAnnotation(DeleteMapping.class) ;
                    String requestMethod = null ;
                    String path = null ;
                    if (getAnnotation!=null){
                        requestMethod = "get" ;
                        path = getAnnotation.value() ;
                    }else if (postAnnotation!=null){
                        requestMethod = "post" ;
                        path = postAnnotation.value() ;
                    }else if (putAnnotation!=null){
                        requestMethod = "put" ;
                        path = putAnnotation.value() ;
                    }else if (deleteAnnotation!=null){
                        requestMethod = "delete" ;
                        path = deleteAnnotation.value() ;
                    }
                    // 获得请求路径
                    action.setMode("equals");
                    // 统一处理 路径参数问题？
                    if (path.contains("{")) {
                        action.setMode("like");
                        path = path.replaceAll("\\{[a-zA-Z]+}", ".*");
                    }
                    //获得当前方法定义类  获得 实例
                    Class<?> actionCls = method.getDeclaringClass();
                    Object controllerInstance = beanMap.get(actionCls);
                    // 方法定义类
                    action.setExecMethod(method);
                    action.setRequestMethod(requestMethod);
                    action.setPath(path);
                    action.setControllerInstance(controllerInstance);
                    // 存放 ActionList中
                    actionList.add(action) ;
                });
            });

            // 分路： 精确匹配  模糊匹配
            equalsActionList = actionList.stream().filter(action -> action.getMode().equals("equals")).collect(Collectors.toList());
            likeActionList = actionList.stream().filter(action -> action.getMode().equals("like")).collect(Collectors.toList());
            ///////////////////////3.请求路径 Action 结束/////////////////////////////////////
        } catch (Exception e) {
            e.printStackTrace();
            log.error("工厂加载异常:"+e.getMessage());
        }
    }

    /**
     * 获得 Bean的实例
     * @param cls 指定类型： 面向接口编程
     * @param <T>
     * @return  返回new实例
     */
    public static <T> T getBean(Class<T> cls){
        // 从被管理的bean中获得 实例
        Object instance = beanMap.get(cls);
        // 判断 是否拥有 @Transactional 如果没有 直接返回实例
        Transactional annotation = instance.getClass().getAnnotation(Transactional.class);
        if (annotation==null) {
            return (T) instance;
        }
        //拥有该注解 使用事务代理
        return TxProxy.getProxy(instance) ;
    }

    /**
     * 根据 请求路径 返回 Action对象
     * @param requestURI  请求路径
     * @param requestMethod  请求路径
     * @return  自定义Action
     */
    public static Action getAction(String requestURI,String requestMethod){
        //1. 尝试精确匹配
        for (Action action : equalsActionList) {
            // 请求路径  请求方法 必须一致
            if (requestURI.equals(action.getPath()) && action.getRequestMethod().equals(requestMethod)){
                return action ;
            }
        }

        //2.再使用 正则匹配
        for (Action action : likeActionList) {
            // 模糊 优先级： 判断长度 匹配正则  请求方法
            if (requestURI.split("/").length == action.getPath().split("/").length
                    && requestURI.matches(action.getPath()) && action.getRequestMethod().equals(requestMethod)){
                return action ;
            }
        }
        throw new RuntimeException("找不到匹配的路径！！！") ;
    }
}
